﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;

namespace AppFuscator.MapDecoder
{
    public partial class SearchForm : Form
    {
        public SearchForm()
        {
            InitializeComponent();
        }

        private readonly XmlReader xmlReader = new XmlReader();
        private Project tree;
        private readonly Parser parser = new Parser();

        private string OldText { get; set; }

        private string NewText { get; set; }

        private IDictionary<Method, string> FoundMethod { get; set; }

        private List<string> ListAssembleName { get; set; }

        public event EventHandler itemChanged;

        private void SearchForm_DragEnter(object sender, DragEventArgs e)
        {
            e.Effect = e.Data.GetDataPresent(DataFormats.FileDrop) ? DragDropEffects.All : DragDropEffects.None;
        }

        private void SearchForm_DragDrop(object sender, DragEventArgs e)
        {
            string[] path = (string[])e.Data.GetData(DataFormats.FileDrop, false);
            if (path.Length > 1)
            {
                MessageBox.Show("You can open only one file.");
                return;
            }
            var fn = path.Last();
            LoadMapFile(fn);
        }

        private void LoadMapFile(string fn)
        {
            try
            {
                tree = xmlReader.FillTree(fn);
                //lblLoad.Text = "Map loaded";
                SetCaption(fn);
            }
            catch
            {
                MessageBox.Show("Map loading error occurred. Please select correct map file.");
            }
        }

        private void btnFilePick_Click(object sender, EventArgs e)
        {
            ShowDialog();
        }

        private void ShowDialog()
        {
            //fileDialog.Title = "Выберите XML файл";
            if (fileDialog.ShowDialog() == DialogResult.OK)
            {
                LoadMapFile(fileDialog.FileNames.Last());
            }
        }

        private void SetCaption(string additionString)
        {
            this.Text = "Appfucator Stack-trace decoder";
            if (additionString != null)
                this.Text += " - " + additionString;
        }

        private bool txtSearchChangedUser = true;

        private void txtSearch_TextChanged(object sender, EventArgs e)
        {
            if (tree != null)
            txtSearchSetChange();
        }

        private void txtSearchSetChange()
        {
            if (txtSearchChangedUser)
            {
                OldText = txtSearch.Text;
                groupBox2.Controls.Clear();
                listCheckBox.Clear();
                txtSearch.SelectionColor = System.Drawing.Color.Black;
                groupBox2.Visible = false;
                if (tree == null)
                    ShowDialog();
                var result = GetAssembleName();
                if (result !=null)
                {
                    txtSearch.Text = result;
                }
            }
            SetTextColor();
        }

        private void SetTextColor()
        {
            if (txtSearchMethods.MethodName != null)
                foreach (var dict in txtSearchMethods.MethodName)
                {
                    var pos = txtSearch.Text.IndexOf(dict.Key);
                    if (pos != -1)
                    {
                        var length = dict.Key.Length;
                        System.Drawing.Color color = new System.Drawing.Color();
                        foreach (var checkBox in listCheckBox)
                        {
                            if (dict.Value == checkBox.Text)
                                color = checkBox.ForeColor;
                        }
                        txtSearch.SelectionStart = pos;
                        txtSearch.SelectionLength = length;
                        txtSearch.SelectionColor = color;
                    }
                }
            //txtSearch.SelectionStart = 0;
            //txtSearch.SelectionLength = 0;
        }

        private void fileDialog_FileOk(object sender, System.ComponentModel.CancelEventArgs e)
        {

        }

        #region Search equals
        private int chkItems = 0;
        private List<CheckBox> listCheckBox = new List<CheckBox>();

        private TxtSearchMethods txtSearchMethods;

        private string GetAssembleName() //Если поиск нашёл 2 и более совпадающих метода, то добавляю чекбоксы с именами сборок у найденных методов
        {
            FoundMethod = tree.SearchAllMethodsByName(OldText);
            ListAssembleName = new List<string>();
            foreach (var mtd in FoundMethod)
            {
                var assName = mtd.Key.NodeLink.AssembleName;
                if (!ListAssembleName.Contains(assName))
                {
                    ListAssembleName.Add(assName);
                }
            }
            if (ListAssembleName.Count > 1)
            {
                groupBox2.Visible = true;
                int delta = 0;
                pushBtnAdded = false;
                foreach (var assembleName in ListAssembleName)
                {
                    listCheckBox.Add(PushButtonAdd(assembleName, delta));
                    delta += 120;
                }
                pushBtnAdded = true;
                NewText = CheckBox_SetChange();
            }
            else
            {
                NewText = OldText;
                foreach (var mtd in FoundMethod)
                {
                    NewText = NewText.Replace(mtd.Value, mtd.Key.NodeLink.FullName);
                }
            }

            txtSearchChangedUser = true;
            return NewText == OldText ? null : NewText;
        }

        private bool pushBtnAdded = false;
        private CheckBox PushButtonAdd(string Name, int deltaWidth)
        {
            CheckBox checkBox = new CheckBox
            {
                Appearance = Appearance.Button,
                AutoSize = true,
                Location = new System.Drawing.Point(20 + deltaWidth, 19)
            };
            checkBox.CheckedChanged += checkBox_CheckedChanged;
            checkBox.ForeColor = Color.GetRandomColor();
            //checkBox.Font = new Font(new FontFamily(GenericFontFamilies.Serif), 12);
            checkBox.CheckState = CheckState.Checked;
            checkBox.Text = Name;
            checkBox.UseVisualStyleBackColor = true;
            groupBox2.Controls.Add(checkBox);
            return checkBox;
        }

        private void checkBox_CheckedChanged(object sender, EventArgs e)
        {
            if (!pushBtnAdded) return;
            var result = CheckBox_SetChange();
            if (result.Length == 0)
                result = OldText;
            txtSearch.Text = result;
            txtSearchChangedUser = true;
        }

        private string CheckBox_SetChange()
        {
            txtSearchMethods = new TxtSearchMethods()
            {
                MethodName = new Dictionary<string, string>()
            };
            txtSearchChangedUser = false;
            NewText = OldText;
            txtSearch.Text = OldText;
            var listMethods = new List<Method>();
            var listReplace = new List<string>();
            foreach (var mtd in FoundMethod)
            {
                listMethods.Add(mtd.Key);
                listReplace.Add(mtd.Value);
            }
            string result = "";
            GetEqualMethodsName(listMethods, listReplace, true, NewText, ref result);

            return result;
        }

        private string GetEqualMethodsName(List<Method> foundMethod, List<string> replace, bool flagReplace, string text, ref string textOut)
        {
            //поиск всех совпадающих имён и замена их в тексте через запятую
            string separator = ", ";
            string tempText = "";
            foreach (var mtd in foundMethod)
            {
                foreach (var select in listCheckBox)
                {
                    if ((mtd.NodeLink.AssembleName == select.Text) && (select.Checked))
                    {
                        tempText = mtd.NodeLink.FullName;
                        if (!txtSearchMethods.MethodName.ContainsKey(tempText))
                            txtSearchMethods.MethodName.Add(tempText, mtd.NodeLink.AssembleName); //todo
                        int index = foundMethod.IndexOf(mtd) + 1;
                        int length = foundMethod.Count - index;
                        List<Method> newList = foundMethod.GetRange(index, length);
                        List<string> newReplace = replace.GetRange(index, length);
                        List<string> listName = new List<string>();
                        foreach (Method method in newList)
                        {
                            listName.Add(string.Format("{0}({1})", method.Name, string.Join(", ", method.Parameters)));
                        }
                        int i = listName.IndexOf(string.Format("{0}({1})", mtd.Name, string.Join(", ", mtd.Parameters)));
                        if (i != -1)
                        {
                            newList = newList.GetRange(i, 1);
                            newReplace = newReplace.GetRange(i, 1);
                            var result = GetEqualMethodsName(newList, newReplace, false, text, ref textOut);
                            if (result.Length > 0)
                                tempText += separator + result;
                        }
                    }
                }
                if (flagReplace)
                {
                    if (tempText.Length > 0)
                    {
                        int index = foundMethod.IndexOf(mtd);
                        text = text.Replace(replace[index], tempText);
                        textOut = text;
                        tempText = "";
                    }
                }
            }
            return tempText;
        }
        #endregion
    }

    struct TxtSearchMethods
    {
        public IDictionary<string, string> MethodName;
    }
}